
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Diagnostics;
using System.Collections;
using System.Xml;


using Swaf.BizObj;
using Swaf.BizRule;
using Swaf.Container;

namespace Swaf.Gui
{
	/// <summary>
	/// Summary description for BaseConfigView.
	/// </summary>
	public abstract class BaseConfigView : IView
	{
		protected Swaf.IApplication m_app;
		protected ILayoutManager m_mainLayout;
		protected object m_mainArea;
		protected IViewHandler m_handler;

		protected IMap m_doc = null;
		protected IDictionary m_namedItems = null;

		protected XmlDocument m_viewTemplate;

		public BaseConfigView(Swaf.IApplication app, object mainArea, IViewHandler handler)
		{
			m_app = app;
			m_mainArea = mainArea;
			m_namedItems = new Hashtable();
			m_namedItems["appHandler"] = handler;
			m_handler = handler;
		}

		public void disconnect()
		{
		}

		public IMap Document {get{return m_doc;}}

		public virtual XmlElement LayoutTemplate
		{
			get
			{
				if(m_viewTemplate != null)
					return m_viewTemplate.DocumentElement;
				else
					return null;
			}
			set
			{
				m_viewTemplate = new XmlDocument();
				m_viewTemplate.AppendChild(m_viewTemplate.ImportNode(value, true));
				createView();
			}
		}

		public virtual void defineNamedItem(string name, IGuiElement item)
		{
			if(NameAddress.isSingleAddress(name))
			{
				m_namedItems[name] = item;
			}
		}

		public virtual object namedObject(string name)
		{
			return m_namedItems[name];
		}
		public virtual IGuiElement namedElement(string name)
		{
			string myName = NameAddress.getFirstAddress(name);
			IGuiElement el = (IGuiElement)m_namedItems[myName];
			if(el == null)
				el = m_mainLayout.getNamedItem(name);
			else if(!NameAddress.isSingleAddress(name))
			{
				if(el is ILayoutManager)
					el = ((ILayoutManager)el).getNamedItem(NameAddress.getRemainingAddress(name));
			}
			return el;
		}

		public virtual void addChild(object child, string location)
		{
			m_mainLayout.addChild(child, location);
		}

		public virtual void applyContent(object context)
		{
			if(m_mainLayout != null)
				m_mainLayout.applyContent(context);
		}

		protected virtual void createView()
		{
			Debug.Assert(m_viewTemplate != null);

			XmlElement viewInfo = m_viewTemplate.DocumentElement;
			if(viewInfo.Name != "View")
				throw new Exception("Invalid XML document format for view layout template");

			XmlElement mainLayoutInfo = (XmlElement)viewInfo.FirstChild;

			try
			{
				m_mainLayout = (ILayoutManager)m_app.guiMgr.createWidget(mainLayoutInfo);
				m_mainLayout.show(createLocation("",m_mainArea));

				XmlNodeList childNodes = viewInfo.ChildNodes;
				//Start at 1 versus 0 because we already used the 0th element for the main layout.
				for(int pos = 1; pos < childNodes.Count; ++pos)
				{
					XmlElement el = (XmlElement)childNodes[pos];
					try
					{
						if(el.Name == "BusinessProcesses")
							createBusinessProcesses(el);
						else if(el.Name == "Document")
						{
							m_doc = new FlexiMap(el);
							foreach(DictionaryEntry entry in m_doc)
							{
								if(entry.Value is IBizObjRules)
								{
									IBizObjRules boRules = (IBizObjRules)entry.Value;
									boRules.defineDocumentContext(m_app, m_doc);
								}
							}
						}
						else if(el.Name == "CommandSet")
							processCommandSet(el);
						else if(el.Name == "BusinessProcessEvents")
							processBusinessProcessEvents(el);
					}
					catch(Exception e)
					{
						//It turns out that it is a real pain to have exceptions kill this process because
						//A GUI client app can be rendered completely useless because of an unhandles exception
						//in a poorly written business process.  Rather then shutdown the entire view and stop,
						//This exception handler attempts to continue processing the view's configuration and 
						//logs the event.
						//TODO: Use .NET's trace level stuff to throw an exception now if config set to do it.
						m_app.Log.println(Application.DEFAULT_FRAMEWORK_LOG_LEVEL,"Severe failure while processing the view config.\r\n {0}", e.ToString());
						if(e.InnerException != null)
						{
							m_app.Log.println(Application.DEFAULT_FRAMEWORK_LOG_LEVEL,"The severe failure was caused by:\r\n{0}", e.InnerException.ToString());
						}
					}
				}
			}
			catch(Exception e)
			{
				throw e;
				//TODO do something...
			}
		}

		protected abstract ILocation createLocation(string address, object parent);
		public virtual void close()
		{
		}

		public string Id
		{
			get
			{
				Debug.Assert(m_viewTemplate != null);
				return m_viewTemplate.DocumentElement.GetAttribute("id");
			}
		}

		public string ShortName
		{
			get
			{
				Debug.Assert(m_viewTemplate != null);
				return m_viewTemplate.DocumentElement.GetAttribute("name");
			}
		}
		public string MediumName
		{
			get
			{
				Debug.Assert(m_viewTemplate != null);
				return m_viewTemplate.DocumentElement.GetAttribute("mediumName");
			}
		}

		public string Description
		{
			get
			{
				Debug.Assert(m_viewTemplate != null);
				return m_viewTemplate.DocumentElement.GetAttribute("description");
			}
		}

		public XmlElement getLayoutTemplate(string templateName)
		{
			object objTemplates = m_doc["layoutTemplates"];
			XmlElement template = null;

			if(objTemplates != null && objTemplates is IDictionary)
			{
				object xmlEl = ((IDictionary)objTemplates)[templateName];
				if(xmlEl != null && xmlEl is XmlElement)
					template = (XmlElement)xmlEl;
			}

			return template;
		}

		protected virtual void createBusinessProcesses(XmlElement el)
		{
			Debug.Assert(el.Name == "BusinessProcesses", "Invalid element type");
			
			IBusinessProcess bp;

			try
			{
				if(!m_namedItems.Contains("appView"))
				{
					bp = m_app.guiMgr.createBusinessProcess("AppViewBusinessProcess", this);
					m_namedItems["appView"] = bp;
				}
			}
			catch(Exception e)
			{
				//It turns out that it is a real pain to have exceptions kill this process because
				//A GUI client app can be rendered completely useless because of an unhandles exception
				//in a poorly written business process.  Rather then shutdown the entire view and stop,
				//This exception handler attempts to continue processing the view's configuration and 
				//logs the event.
				//TODO: Use .NET's trace level stuff to throw an exception now if config set to do it.
				m_app.Log.println(Application.DEFAULT_FRAMEWORK_LOG_LEVEL,"Severe failure while processing the view config.\r\n {0}", e.ToString());
				if(e.InnerException != null)
				{
					m_app.Log.println(Application.DEFAULT_FRAMEWORK_LOG_LEVEL,"The severe failure was caused by:\r\n{0}", e.InnerException.ToString());
				}
			}


			IDictionary layoutTemplates = new Hashtable();
			IList ruleParams = new ArrayList();
			foreach(XmlElement process in el.ChildNodes)
			{
				foreach(XmlElement childInfo in process.ChildNodes)
					try
					{
						if(childInfo.HasAttribute("template"))
							ruleParams.Add(childInfo.GetAttribute("template"));
					}
					catch(Exception e)
					{
						//It turns out that it is a real pain to have exceptions kill this process because
						//A GUI client app can be rendered completely useless because of an unhandles exception
						//in a poorly written business process.  Rather then shutdown the entire view and stop,
						//This exception handler attempts to continue processing the view's configuration and 
						//logs the event.
						//TODO: Use .NET's trace level stuff to throw an exception now if config set to do it.
						m_app.Log.println(Application.DEFAULT_FRAMEWORK_LOG_LEVEL,"Severe failure while processing the view config.\r\n {0}", e.ToString());
						if(e.InnerException != null)
						{
							m_app.Log.println(Application.DEFAULT_FRAMEWORK_LOG_LEVEL,"The severe failure was caused by:\r\n{0}", e.InnerException.ToString());
						}
					}

			}
			IResults rc = m_app.ruleMgr.execute("getDisplayTemplate", ruleParams, true);
			for(int pos = 0; pos < rc.numberOfResults; ++pos)
			{
				if(rc.getExceptionInfo(pos) == null)
					layoutTemplates[ruleParams[pos]] = rc.getCallResults(pos);
			}
			m_doc["layoutTemplates"] = layoutTemplates;

			foreach(XmlElement process in el.ChildNodes)
			{
				try
				{
					//IBusinessProcess bp = (IBusinessProcess)guiBPs.createFromNickName(process.GetAttribute("type"), m_app, this, (XmlElement)process.FirstChild);
					bp = m_app.guiMgr.createBusinessProcess(process, this);
					if(bp == null) throw new Exception("Cannot create business process");
					m_namedItems[bp.Id] = bp;
				}
				catch(Exception e)
				{
					//It turns out that it is a real pain to have exceptions kill this process because
					//A GUI client app can be rendered completely useless because of an unhandles exception
					//in a poorly written business process.  Rather then shutdown the entire view and stop,
					//This exception handler attempts to continue processing the view's configuration and 
					//logs the event.
					//TODO: Use .NET's trace level stuff to throw an exception now if config set to do it.
					m_app.Log.println(Application.DEFAULT_FRAMEWORK_LOG_LEVEL,"Severe failure while processing the view config.\r\n {0}", e.ToString());
					if(e.InnerException != null)
					{
						m_app.Log.println(Application.DEFAULT_FRAMEWORK_LOG_LEVEL,"The severe failure was caused by:\r\n{0}", e.InnerException.ToString());
					}
				}
			}
		}

		protected virtual void processCommandSet(XmlElement commandSetInfo)
		{
			ICommandListDisplay cmdList = (ICommandListDisplay)namedElement(commandSetInfo.GetAttribute("forCommandList"));
			foreach(XmlElement cmdInfo in commandSetInfo.ChildNodes)
			{
				Command cmdObj = new Command(this, cmdInfo);
				cmdList.addCommand(cmdObj);
			}
		}

		protected virtual void processBusinessProcessEvents(XmlElement events)
		{
			foreach(XmlNode n in events.ChildNodes)
			{
				if(n is XmlElement)
				{
					XmlElement el = (XmlElement)n;
					if(el.Name == "Event")
					{
						string[] obParts = el.GetAttribute("name").Split('.');
						string[] acParts = el.GetAttribute("action").Split('.');
						if(obParts.Length > 1 && acParts.Length > 1)
						{
							object observerBp = m_namedItems[obParts[0]];
							object actionBp = m_namedItems[acParts[0]];
							if(observerBp != null && actionBp != null)
							{
								Cmn.addEventObserver(observerBp, obParts[1], actionBp, acParts[1]);
							}
						}
					}
				}
			}
		}
	}
}
